home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Crypt / RSA.php < prev    next >
Encoding:
PHP Script  |  2005-12-02  |  19.6 KB  |  537 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. /**
  5.  * Crypt_RSA allows to do following operations:
  6.  *     - key pair generation
  7.  *     - encryption and decryption
  8.  *     - signing and sign validation
  9.  *
  10.  * PHP versions 4 and 5
  11.  *
  12.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  13.  * that is available through the world-wide-web at the following URI:
  14.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  15.  * the PHP License and are unable to obtain it through the web, please
  16.  * send a note to license@php.net so we can mail you a copy immediately.
  17.  *
  18.  * @category   Encryption
  19.  * @package    Crypt_RSA
  20.  * @author     Alexander Valyalkin <valyala@gmail.com>
  21.  * @copyright  2005 Alexander Valyalkin
  22.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  23.  * @version    1.0.0
  24.  * @link       http://pear.php.net/package/Crypt_RSA
  25.  */
  26.  
  27. /**
  28.  * RSA error handling facilities
  29.  */
  30. require_once 'Crypt/RSA/ErrorHandler.php';
  31.  
  32. /**
  33.  * loader for math wrappers
  34.  */
  35. require_once 'Crypt/RSA/MathLoader.php';
  36.  
  37. /**
  38.  * helper class for mange single key
  39.  */
  40. require_once 'Crypt/RSA/Key.php';
  41.  
  42. /**
  43.  * helper class for manage key pair
  44.  */
  45. require_once 'Crypt/RSA/KeyPair.php';
  46.  
  47. /**
  48.  * Crypt_RSA class, derived from Crypt_RSA_ErrorHandler
  49.  *
  50.  * Provides the following functions:
  51.  *  - setParams($params) - sets parameters of current object
  52.  *  - encrypt($plain_data, $key = null) - encrypts data
  53.  *  - decrypt($enc_data, $key = null) - decrypts data
  54.  *  - createSign($doc, $private_key = null) - signs document by private key
  55.  *  - validateSign($doc, $signature, $public_key = null) - validates signature of document
  56.  *
  57.  * Example usage:
  58.  *     // creating an error handler
  59.  *     $error_handler = create_function('$obj', 'echo "error: ", $obj->getMessage(), "\n"');
  60.  *
  61.  *     // 1024-bit key pair generation
  62.  *     $key_pair = new Crypt_RSA_KeyPair(1024);
  63.  *
  64.  *     // check consistence of Crypt_RSA_KeyPair object
  65.  *     $error_handler($rsa_obj);
  66.  *
  67.  *     // creating Crypt_RSA object
  68.  *     $rsa_obj = new Crypt_RSA;
  69.  *
  70.  *     // check consistence of Crypt_RSA object
  71.  *     $error_handler($rsa_obj);
  72.  *
  73.  *     // set error handler on Crypt_RSA object ( see Crypt/RSA/ErrorHandler.php for details )
  74.  *     $rsa_obj->setErrorHandler($error_handler);
  75.  *
  76.  *     // encryption (usually using public key)
  77.  *     $enc_data = $rsa_obj->encrypt($plain_data, $key_pair->getPublicKey());
  78.  *
  79.  *     // decryption (usually using private key)
  80.  *     $plain_data = $rsa_obj->decrypt($enc_data, $key_pair->getPrivateKey());
  81.  *
  82.  *     // signing
  83.  *     $signature = $rsa_obj->createSign($document, $key_pair->getPrivateKey());
  84.  *
  85.  *     // signature checking
  86.  *     $is_valid = $rsa_obj->validateSign($document, $signature, $key_pair->getPublicKey());
  87.  *
  88.  *     // signing many documents by one private key
  89.  *     $rsa_obj = new Crypt_RSA(array('private_key' => $key_pair->getPrivateKey()));
  90.  *     // check consistence of Crypt_RSA object
  91.  *     $error_handler($rsa_obj);
  92.  *     // set error handler ( see Crypt/RSA/ErrorHandler.php for details )
  93.  *     $rsa_obj->setErrorHandler($error_handler);
  94.  *     // sign many documents
  95.  *     $sign_1 = $rsa_obj->sign($doc_1);
  96.  *     $sign_2 = $rsa_obj->sign($doc_2);
  97.  *     //...
  98.  *     $sign_n = $rsa_obj->sign($doc_n);
  99.  *
  100.  *     // changing default hash function, which is used for sign
  101.  *     // creating/validation
  102.  *     $rsa_obj->setParams(array('hash_func' => 'md5'));
  103.  *
  104.  *     // using factory() method instead of constructor (it returns PEAR_Error object on failure)
  105.  *     $rsa_obj = &Crypt_RSA::factory();
  106.  *     if (PEAR::isError($rsa_obj)) {
  107.  *         echo "error: ", $rsa_obj->getMessage(), "\n";
  108.  *     }
  109.  *
  110.  * @category   Encryption
  111.  * @package    Crypt_RSA
  112.  * @author     Alexander Valyalkin <valyala@gmail.com>
  113.  * @copyright  2005 Alexander Valyalkin
  114.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  115.  * @link       http://pear.php.net/package/Crypt_RSA
  116.  * @version    @package_version@
  117.  * @access     public
  118.  */
  119. class Crypt_RSA extends Crypt_RSA_ErrorHandler
  120. {
  121.     /**
  122.      * Reference to math wrapper, which is used to
  123.      * manipulate large integers in RSA algorithm.
  124.      *
  125.      * @var object of Crypt_RSA_Math_* class
  126.      * @access private
  127.      */
  128.     var $_math_obj;
  129.  
  130.     /**
  131.      * key for encryption, which is used by encrypt() method
  132.      *
  133.      * @var object of Crypt_RSA_KEY class
  134.      * @access private
  135.      */
  136.     var $_enc_key;
  137.  
  138.     /**
  139.      * key for decryption, which is used by decrypt() method
  140.      *
  141.      * @var object of Crypt_RSA_KEY class
  142.      * @access private
  143.      */
  144.     var $_dec_key;
  145.  
  146.     /**
  147.      * public key, which is used by validateSign() method
  148.      *
  149.      * @var object of Crypt_RSA_KEY class
  150.      * @access private
  151.      */
  152.     var $_public_key;
  153.  
  154.     /**
  155.      * private key, which is used by createSign() method
  156.      *
  157.      * @var object of Crypt_RSA_KEY class
  158.      * @access private
  159.      */
  160.     var $_private_key;
  161.  
  162.     /**
  163.      * name of hash function, which is used by validateSign()
  164.      * and createSign() methods. Default hash function is SHA-1
  165.      *
  166.      * @var string
  167.      * @access private
  168.      */
  169.     var $_hash_func = 'sha1';
  170.  
  171.     /**
  172.      * Crypt_RSA constructor.
  173.      *
  174.      * @param array $params
  175.      *        Optional associative array of parameters, such as:
  176.      *        enc_key, dec_key, private_key, public_key, hash_func.
  177.      *        See setParams() method for more detailed description of
  178.      *        these parameters.
  179.      *
  180.      * @param string $wrapper_name
  181.      *        Name of math wrapper, which will be used to
  182.      *        perform different operations with big integers.
  183.      *        See contents of Crypt/RSA/Math folder for examples of wrappers.
  184.      *        Read docs/Crypt_RSA/docs/math_wrappers.txt for details.
  185.      *
  186.      * @param string $error_handler   name of error handler function
  187.      *
  188.      * @access public
  189.      */
  190.     function Crypt_RSA($params = null, $wrapper_name = 'default', $error_handler = '')
  191.     {
  192.         // set error handler
  193.         $this->setErrorHandler($error_handler);
  194.         // try to load math wrapper
  195.         $obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name);
  196.         if (PEAR::isError($obj)) {
  197.             // error during loading of math wrapper
  198.             // Crypt_RSA object is partially constructed.
  199.             $this->pushError($obj);
  200.             return;
  201.         }
  202.         $this->_math_obj = &$obj;
  203.  
  204.         if (!is_null($params)) {
  205.             if (!$this->setParams($params)) {
  206.                 // error in Crypt_RSA::setParams() function
  207.                 return;
  208.             }
  209.         }
  210.     }
  211.  
  212.     /**
  213.      * Crypt_RSA factory.
  214.      *
  215.      * @param array $params
  216.      *        Optional associative array of parameters, such as:
  217.      *        enc_key, dec_key, private_key, public_key, hash_func.
  218.      *        See setParams() method for more detailed description of
  219.      *        these parameters.
  220.      *
  221.      * @param string $wrapper_name
  222.      *        Name of math wrapper, which will be used to
  223.      *        perform different operations with big integers.
  224.      *        See contents of Crypt/RSA/Math folder for examples of wrappers.
  225.      *        Read docs/Crypt_RSA/docs/math_wrappers.txt for details.
  226.      *
  227.      * @return object  new Crypt_RSA object on success or PEAR_Error object on failure
  228.      * @access public
  229.      */
  230.     function &factory($params = null, $wrapper_name = 'default')
  231.     {
  232.         $obj = &new Crypt_RSA($params, $wrapper_name);
  233.         if ($obj->isError()) {
  234.             // error during creating a new object. Retrurn PEAR_Error object
  235.             return $obj->getLastError();
  236.         }
  237.         // object created successfully. Return it
  238.         return $obj;
  239.     }
  240.  
  241.     /**
  242.      * Accepts any combination of available parameters as associative array:
  243.      *     enc_key - encryption key for encrypt() method
  244.      *     dec_key - decryption key for decrypt() method
  245.      *     public_key - key for validateSign() method
  246.      *     private_key - key for createSign() method
  247.      *     hash_func - name of hash function, which will be used to create and validate sign
  248.      *
  249.      * @param array $params
  250.      *        associative array of permitted parameters (see above)
  251.      *
  252.      * @return bool   true on success or false on error
  253.      * @access public
  254.      */
  255.     function setParams($params)
  256.     {
  257.         if (!is_array($params)) {
  258.             $obj = PEAR::raiseError('parameters must be passed to function as associative array', CRYPT_RSA_ERROR_WRONG_PARAMS);
  259.             $this->pushError($obj);
  260.             return false;
  261.         }
  262.  
  263.         if (isset($params['enc_key'])) {
  264.             if (Crypt_RSA_Key::isValid($params['enc_key'])) {
  265.                 $this->_enc_key = $params['enc_key'];
  266.             } else {
  267.                 $obj = PEAR::raiseError('wrong encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
  268.                 $this->pushError($obj);
  269.                 return false;
  270.             }
  271.         }
  272.         if (isset($params['dec_key'])) {
  273.             if (Crypt_RSA_Key::isValid($params['dec_key'])) {
  274.                 $this->_dec_key = $params['dec_key'];
  275.             } else {
  276.                 $obj = PEAR::raiseError('wrong decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
  277.                 $this->pushError($obj);
  278.                 return false;
  279.             }
  280.         }
  281.         if (isset($params['private_key'])) {
  282.             if (Crypt_RSA_Key::isValid($params['private_key'])) {
  283.                 if ($params['private_key']->getKeyType() != 'private') {
  284.                     $obj = PEAR::raiseError('private key must have "private" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
  285.                     $this->pushError($obj);
  286.                     return false;
  287.                 }
  288.                 $this->_private_key = $params['private_key'];
  289.             } else {
  290.                 $obj = PEAR::raiseError('wrong private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
  291.                 $this->pushError($obj);
  292.                 return false;
  293.             }
  294.         }
  295.         if (isset($params['public_key'])) {
  296.             if (Crypt_RSA_Key::isValid($params['public_key'])) {
  297.                 if ($params['public_key']->getKeyType() != 'public') {
  298.                     $obj = PEAR::raiseError('public key must have "public" attribute', CRYPT_RSA_ERROR_WRONG_KEY_TYPE);
  299.                     $this->pushError($obj);
  300.                     return false;
  301.                 }
  302.                 $this->_public_key = $params['public_key'];
  303.             } else {
  304.                 $obj = PEAR::raiseError('wrong public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
  305.                 $this->pushError($obj);
  306.                 return false;
  307.             }
  308.         }
  309.         if (isset($params['hash_func'])) {
  310.             if (!function_exists($params['hash_func'])) {
  311.                 $obj = PEAR::raiseError('cannot find hash function with name [' . $params['hash_func'] . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
  312.                 $this->pushError($obj);
  313.                 return false;
  314.             }
  315.             $this->_hash_func = $params['hash_func'];
  316.         }
  317.         return true; // all ok
  318.     }
  319.  
  320.     /**
  321.      * Ecnrypts $plain_data by the key $this->_enc_key or $key.
  322.      *
  323.      * @param string $plain_data  data, which must be encrypted
  324.      * @param object $key         encryption key (object of Crypt_RSA_Key class)
  325.      * @return mixed
  326.      *         encrypted data as string on success or false on error
  327.      *
  328.      * @access public
  329.      */
  330.     function encrypt($plain_data, $key = null)
  331.     {
  332.         $enc_data = $this->encryptBinary($plain_data, $key);
  333.         if ($enc_data !== false) {
  334.             return base64_encode($enc_data);
  335.         }
  336.         // error during encripting data
  337.         return false;
  338.     }
  339.  
  340.     /**
  341.      * Ecnrypts $plain_data by the key $this->_enc_key or $key.
  342.      *
  343.      * @param string $plain_data  data, which must be encrypted
  344.      * @param object $key         encryption key (object of Crypt_RSA_Key class)
  345.      * @return mixed
  346.      *         encrypted data as binary string on success or false on error
  347.      *
  348.      * @access public
  349.      */
  350.     function encryptBinary($plain_data, $key = null)
  351.     {
  352.         if (is_null($key)) {
  353.             // use current encryption key
  354.             $key = $this->_enc_key;
  355.         } elseif (!Crypt_RSA_Key::isValid($key)) {
  356.             $obj = PEAR::raiseError('invalid encryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
  357.             $this->pushError($obj);
  358.             return false;
  359.         }
  360.  
  361.         // append tail \x01 to plain data. It needs for correctly decrypting of data
  362.         $plain_data .= "\x01";
  363.  
  364.         $plain_data = $this->_math_obj->bin2int($plain_data);
  365.         $exp = $this->_math_obj->bin2int($key->getExponent());
  366.         $modulus = $this->_math_obj->bin2int($key->getModulus());
  367.  
  368.         // divide plain data into chunks
  369.         $data_len = $this->_math_obj->bitLen($plain_data);
  370.         $chunk_len = $key->getKeyLength() - 1;
  371.         $block_len = (int) ceil($chunk_len / 8);
  372.         $curr_pos = 0;
  373.         $enc_data = '';
  374.         while ($curr_pos < $data_len) {
  375.             $tmp = $this->_math_obj->subint($plain_data, $curr_pos, $chunk_len);
  376.             $enc_data .= str_pad(
  377.                 $this->_math_obj->int2bin($this->_math_obj->powmod($tmp, $exp, $modulus)),
  378.                 $block_len,
  379.                 "\0"
  380.             );
  381.             $curr_pos += $chunk_len;
  382.         }
  383.         return $enc_data;
  384.     }
  385.  
  386.     /**
  387.      * Decrypts $enc_data by the key $this->_dec_key or $key.
  388.      *
  389.      * @param string $enc_data  encrypted data as string
  390.      * @param object $key       decryption key (object of RSA_Crypt_Key class)
  391.      * @return mixed
  392.      *         decrypted data as string on success or false on error
  393.      *
  394.      * @access public
  395.      */
  396.     function decrypt($enc_data, $key = null)
  397.     {
  398.         $enc_data = base64_decode($enc_data);
  399.         return $this->decryptBinary($enc_data, $key);
  400.     }
  401.  
  402.     /**
  403.      * Decrypts $enc_data by the key $this->_dec_key or $key.
  404.      *
  405.      * @param string $enc_data  encrypted data as binary string
  406.      * @param object $key       decryption key (object of RSA_Crypt_Key class)
  407.      * @return mixed
  408.      *         decrypted data as string on success or false on error
  409.      *
  410.      * @access public
  411.      */
  412.     function decryptBinary($enc_data, $key = null)
  413.     {
  414.         if (is_null($key)) {
  415.             // use current decryption key
  416.             $key = $this->_dec_key;
  417.         } elseif (!Crypt_RSA_Key::isValid($key)) {
  418.             $obj = PEAR::raiseError('invalid decryption key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
  419.             $this->pushError($obj);
  420.             return false;
  421.         }
  422.  
  423.         $exp = $this->_math_obj->bin2int($key->getExponent());
  424.         $modulus = $this->_math_obj->bin2int($key->getModulus());
  425.  
  426.         $data_len = strlen($enc_data);
  427.         $chunk_len = $key->getKeyLength() - 1;
  428.         $block_len = (int) ceil($chunk_len / 8);
  429.         $curr_pos = 0;
  430.         $bit_pos = 0;
  431.         $plain_data = $this->_math_obj->bin2int("\0");
  432.         while ($curr_pos < $data_len) {
  433.             $tmp = $this->_math_obj->bin2int(substr($enc_data, $curr_pos, $block_len));
  434.             $tmp = $this->_math_obj->powmod($tmp, $exp, $modulus);
  435.             $plain_data = $this->_math_obj->bitOr($plain_data, $tmp, $bit_pos);
  436.             $bit_pos += $chunk_len;
  437.             $curr_pos += $block_len;
  438.         }
  439.         $result = $this->_math_obj->int2bin($plain_data);
  440.  
  441.         // delete tail, containing of \x01
  442.         $tail = ord($result{strlen($result) - 1});
  443.         if ($tail != 1) {
  444.             $obj = PEAR::raiseError("Error tail of decrypted text = {$tail}. Expected 1", CRYPT_RSA_ERROR_WRONG_TAIL);
  445.             $this->pushError($obj);
  446.             return false;
  447.         }
  448.         return substr($result, 0, -1);
  449.     }
  450.  
  451.     /**
  452.      * Creates sign for document $document, using $this->_private_key or $private_key
  453.      * as private key and $this->_hash_func or $hash_func as hash function.
  454.      *
  455.      * @param string $document     document, which must be signed
  456.      * @param object $private_key  private key (object of Crypt_RSA_Key type)
  457.      * @param string $hash_func    name of hash function, which will be used during signing
  458.      * @return mixed
  459.      *         signature of $document as string on success or false on error
  460.      *
  461.      * @access public
  462.      */
  463.     function createSign($document, $private_key = null, $hash_func = null)
  464.     {
  465.         // check private key
  466.         if (is_null($private_key)) {
  467.             $private_key = $this->_private_key;
  468.         } elseif (!Crypt_RSA_Key::isValid($private_key)) {
  469.             $obj = PEAR::raiseError('invalid private key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
  470.             $this->pushError($obj);
  471.             return false;
  472.         }
  473.         if ($private_key->getKeyType() != 'private') {
  474.             $obj = PEAR::raiseError('signing key must be private', CRYPT_RSA_ERROR_NEED_PRV_KEY);
  475.             $this->pushError($obj);
  476.             return false;
  477.         }
  478.  
  479.         // check hash_func
  480.         if (is_null($hash_func)) {
  481.             $hash_func = $this->_hash_func;
  482.         }
  483.         if (!function_exists($hash_func)) {
  484.             $obj = PEAR::raiseError('cannot find hash function with name [' . $hash_func . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
  485.             $this->pushError($obj);
  486.             return false;
  487.         }
  488.  
  489.         return $this->encrypt($hash_func($document), $private_key);
  490.     }
  491.  
  492.     /**
  493.      * Validates $signature for document $document with public key $this->_public_key
  494.      * or $public_key and hash function $this->_hash_func or $hash_func.
  495.      *
  496.      * @param string $document    document, signature of which must be validated
  497.      * @param string $signature   signature, which must be validated
  498.      * @param object $public_key  public key (object of Crypt_RSA_Key class)
  499.      * @param string $hash_func   hash function, which will be used during validating signature
  500.      * @return mixed
  501.      *         true, if signature of document is valid
  502.      *         false, if signature of document is invalid
  503.      *         null on error
  504.      *
  505.      * @access public
  506.      */
  507.     function validateSign($document, $signature, $public_key = null, $hash_func = null)
  508.     {
  509.         // check public key
  510.         if (is_null($public_key)) {
  511.             $public_key = $this->_public_key;
  512.         } elseif (!Crypt_RSA_Key::isValid($public_key)) {
  513.             $obj = PEAR::raiseError('invalid public key. It must be an object of Crypt_RSA_Key class', CRYPT_RSA_ERROR_WRONG_KEY);
  514.             $this->pushError($obj);
  515.             return null;
  516.         }
  517.         if ($public_key->getKeyType() != 'public') {
  518.             $obj = PEAR::raiseError('validating key must be public', CRYPT_RSA_ERROR_NEED_PUB_KEY);
  519.             $this->pushError($obj);
  520.             return null;
  521.         }
  522.  
  523.         // check hash_func
  524.         if (is_null($hash_func)) {
  525.             $hash_func = $this->_hash_func;
  526.         }
  527.         if (!function_exists($hash_func)) {
  528.             $obj = PEAR::raiseError('cannot find hash function with name [' . $hash_func . ']', CRYPT_RSA_ERROR_WRONG_HASH_FUNC);
  529.             $this->pushError($obj);
  530.             return null;
  531.         }
  532.  
  533.         return $hash_func($document) == $this->decrypt($signature, $public_key);
  534.     }
  535. }
  536.  
  537. ?>